package com.hulk.dryad.web.initializer;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hulk.dryad.common.annotation.RespExcel;
import com.hulk.dryad.common.annotation.RespSha256;
import com.hulk.dryad.common.component.R;
import com.hulk.dryad.common.constant.SecurityConstants;
import com.hulk.dryad.common.constant.enums.BEC;
import com.hulk.dryad.common.util.ClassUtils;
import com.hulk.dryad.common.util.Sha256Util;
import com.hulk.dryad.manage.provider.cache.SignKeyCache;
import jodd.util.StringPool;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;


import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Method;



/**
 * 响应数据的加密处理<br>
 * 本类只对控制器参数中含有<strong>{@link org.springframework.web.bind.annotation.ResponseBody}</strong>
 * 或者控制类上含有<strong>{@link org.springframework.web.bind.annotation.RestController}</strong>
 * 以及package为<strong><code>com.hulk.dryad</code></strong>下的注解有效
 *
 * @author hulk
 * @version 2020/9/4
 * @see ResponseBodyAdvice
 */
@Slf4j
@Order(Ordered.LOWEST_PRECEDENCE-2)
@RequiredArgsConstructor
@RestControllerAdvice(basePackages = "com.hulk.dryad")
public class ResponseSha256BodyAdvice implements ResponseBodyAdvice<Object> {

	private final ObjectMapper objectMapper;
	private final SignKeyCache signKeyCache;

	@Override
	public boolean supports(MethodParameter returnType, Class converterType) {
		Method method = returnType.getMethod();
		boolean isMethodRespExcelAnnotated = AnnotatedElementUtils.isAnnotated(method, RespExcel.class);
		if(isMethodRespExcelAnnotated){
			return false;
		}
		return ClassUtils.isAnnotated(method, RespSha256.class);
	}

	@SneakyThrows
	@Nullable
	@Override
	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
								  Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

		if (body == null) {
			return null;
		}
		if (body instanceof R<?>) {
			return this.sign(body);
		}
		if (body instanceof BEC) {
			BEC bec = (BEC) body;
			if(bec==BEC.S0){
				return  this.sign(R.ok());
			}else {
				return  this.sign(R.failed(bec.getErrCode(), bec.getErrDesc()));
			}
		}
		return  this.sign(R.ok(body));
	}
	@SneakyThrows
	private R<?> sign(Object obj ){
		R<?> rt =(R)obj ;
		int code = rt.getCode();
		String msg = rt.getMsg();
		String salt = rt.getSalt();
		Object data =rt.getData();
		String dataJson = objectMapper.writeValueAsString(data);
		String key = signKeyCache.getSignKey();

		StringBuilder builder = new StringBuilder();
		builder.append("code").append(StringPool.EQUALS).append(code);
		if(StrUtil.isNotBlank(msg)){
			builder.append(StringPool.AMPERSAND).append("msg").append(StringPool.EQUALS).append(msg);
		}
		if(StrUtil.isNotBlank(salt)){
			builder.append(StringPool.AMPERSAND).append("salt").append(StringPool.EQUALS).append(salt);
		}
		if(ObjectUtil.isEmpty(data)) {
			builder.append(StringPool.AMPERSAND).append("data").append(StringPool.EQUALS).append(dataJson);
		}
		if(StrUtil.isNotBlank(key)){
		    builder.append(StringPool.AMPERSAND).append(SecurityConstants.SIGN_KEY_NAME).append(StringPool.EQUALS).append(key).toString();
		}
		String sign = Sha256Util.sign(builder.toString());
		rt.setSign(sign);
		return rt;
	}

}
